06 |
您所在的位置:网站首页 › spring boot操作日志记录 › 06 |
目录 1. 通知类型 2. 通知顺序 3. 切入点表达式 execution() annotation() 4. 连接点(JoinPoint) 5. 案例:将CRUD接口的相关操作记录到数据库中 AOP: Aspect Oriented Programming (面向切面编程、面向方面编程),其实就是面向特定方法编程 优势:代码无侵入、减少重复代码、提高开发效率、维护方便 环境准备 以下代码是从tlias案例中复制过来的 场景: 1. 案例部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方 法的执行耗时 导入依赖: org.springframework.boot spring-boot-starter-aop定义Aop函数: package pearl.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Slf4j @Component @Aspect public class TimeAspect { @Around("execution(* pearl.service.*.*(..))") public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable { // 1. 记录开始时间 long begin =System.currentTimeMillis(); // 2. 调用原始方法运行 Object result = joinPoint.proceed(); // 3. 记录结束时间,计算方法执行耗时 long end = System.currentTimeMillis(); log.info(joinPoint.getSignature()+"方法执行耗时:{}ms",end-begin); return result; } }完成! 核心概念: 连接点: JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息) 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法) 切入点: PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)目标对象: Target,通知所应用的对象 1. 通知类型 @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行 需要自己调用ProceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标方法执行方法的返回值必须指定为object,来接收原始方法的返回值。@Before:前置通知,此注解标注的通知方法在目标方法前被执行@After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行@AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行@AfterThrowing :异常后通知,此注解标注的通知方法发生异常后执行补充:@Pointcut 该注解的作用是将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可。 2. 通知顺序 3. 切入点表达式概念:描述切入点方法的一种表达式 作用:主要用来决定项目中的哪些方法需要加入通知 常见形式: 1.execution(..…)︰根据方法的签名来匹配 2.@annotation(...) ︰根据注解匹配 execution() annotation()1. 自定义注解: package pearl.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)//指定运行时生效 @Target(ElementType.METHOD)//指定作用到方法上 public @interface MyLog { }2. 在方法前加上自定义的注解 3. 根据注解匹配切入点表达式 4. 连接点(JoinPoint) 5. 案例:将CRUD接口的相关操作记录到数据库中日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长。 步骤: 引入AOP依赖(该文上方有,自行查看)准备数据库表,并引入实体类自定义注解@Log定义切面类,完成记录操作日志的逻辑数据库表: -- 操作日志表 create table operate_log( id int unsigned primary key auto_increment comment 'ID', operate_user int unsigned comment '操作人ID', operate_time datetime comment '操作时间', class_name varchar(100) comment '操作的类名', method_name varchar(100) comment '操作的方法名', method_params varchar(1000) comment '方法参数', return_value varchar(2000) comment '返回值', cost_time bigint comment '方法执行耗时, 单位:ms' ) comment '操作日志表';实体类OperateLog: @Data @NoArgsConstructor @AllArgsConstructor public class OperateLog { private Integer id; //ID private Integer operateUser; //操作人ID private LocalDateTime operateTime; //操作时间 private String className; //操作类名 private String methodName; //操作方法名 private String methodParams; //操作方法参数 private String returnValue; //操作方法返回值 private Long costTime; //操作耗时 }OperateLogMapper接口: @Mapper public interface OperateLogMapper { //插入日志数据 @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " + "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});") public void insert(OperateLog log); }定义注解类: 首先创建一个包anno 然后在包里新建Annotation(注解类)Log @Retention(RetentionPolicy.RUNTIME) //指定运行时生效 @Target(ElementType.METHOD) //指定该注解作用到方法上 public @interface Log { }定义切面类 LogAspect package pearl.aop; import com.alibaba.fastjson.JSONObject; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import pearl.mapper.OperateLogMapper; import pearl.pojo.OperateLog; import pearl.utils.JwtUtils; import javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; import java.util.Arrays; @Slf4j @Component //标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的) @Aspect //切面类 public class LogAspect { @Autowired private OperateLogMapper operateLogMapper; //要调用该类中的insert方法 @Autowired private HttpServletRequest request; @Around("@annotation(pearl.anno.Log)")//@annotation表示匹配注解类,括号中是要匹配注解的全类名,表示使用该注解的方法都会匹配 public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable { // 获取记录到日志中的信息 // 操作人ID // 获取请求头中的jwt令牌,解析令牌 String jwt = request.getHeader("token"); Claims claims = JwtUtils.parseJWT(jwt); Integer operateUserId = (Integer) claims.get("id"); // 操作时间 LocalDateTime operateTime = LocalDateTime.now(); // 操作类名 String className = joinPoint.getTarget().getClass().getName(); // 操作方法名 String methodName = joinPoint.getSignature().getName(); // 操作方法参数 Object[] args = joinPoint.getArgs(); String methodParams = Arrays.toString(args); long begin = System.currentTimeMillis(); //调用原始方法运行 Object result = joinPoint.proceed(); long end = System.currentTimeMillis(); // 操作方法返回值 String returnValue = JSONObject.toJSONString(result); // 操作耗时 Long costTime = end - begin; //记录日志操作 OperateLog operateLog = new OperateLog(null,operateUserId,operateTime,className,methodName,methodParams,returnValue,costTime); operateLogMapper.insert(operateLog); return result; } }在需要的controller接口方法前设置@Log注解即可。(切记不要在登录方法前加)
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |